package com.azier.influxdb.service;

import com.azier.influxdb.mapper.InfluxDBBaseMapper;
import com.azier.influxdb.util.InfluxDBHandleUtil;
import org.influxdb.InfluxDBException;
import org.influxdb.InfluxDBMapperException;
import org.influxdb.annotation.Column;
import org.influxdb.dto.Query;
import org.influxdb.dto.QueryResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

@Service
public abstract class InfluxDBBaseService<T> {

    @Autowired
    protected InfluxDBBaseMapper influxDBBaseMapper ;

    @Value("${influxDB.database:seem_47}")
    private String              publicDatabase;

    @Value("${influxDB.retentionPolicy:autogen}")
    private String              publicRetentionPolicy;

    public static final String ORDER_DESC = "desc";

    public static final String ORDER_ASC = "asc";
    /** 定义泛型的class */

    private Class type;

    //在构造方法中初始化泛型class
    public InfluxDBBaseService() {
        this.type = this.getTypeClass();
    }
    /**
     * 获得泛型的class的方法
     * @return
     */
    @SuppressWarnings("rawtypes")
    private Class getTypeClass(){
        Class clazz = (Class)((ParameterizedType) this.getClass().getGenericSuperclass())
            .getActualTypeArguments()[0];
        return clazz;
    }

    /**
     * 获取泛型中的Measurement、database、retentionPolicy、timeUnit
     * @return
     */
    public String getMeasurementName() {
        return getMeasurementName(type);
    }
    public String getMeasurementName(Class clazz) {
        return InfluxDBHandleUtil.getMeasurementName(clazz);
    }
    public String getDatabaseName() {
        return getDatabaseName(type);
    }

    /**
     * 获取类上声明的database
     * @param clazz
     * @return
     */
    public String getDatabaseName(Class clazz) {
        return InfluxDBHandleUtil.getDatabaseName(clazz);
    }
    /**
     * 获取配置文件公共声明的database
     * @return
     */
    public String getPublicDatabaseName() {
        return publicDatabase;
    }
    /**
     * 获取配置文件公共声明的RetentionPolicy
     * @return
     */
    public String getPublicRetentionPolicy() {
        return publicRetentionPolicy;
    }
    public String getRetentionPolicy() {
        return getRetentionPolicy(type);
    }
    public String getRetentionPolicy(Class clazz) {
        return InfluxDBHandleUtil.getRetentionPolicy(clazz);
    }
    public TimeUnit getTimeUnit() {
        return getTimeUnit(type);
    }
    public TimeUnit getTimeUnit(Class clazz) {
        return InfluxDBHandleUtil.getTimeUnit(clazz);
    }
    public ConcurrentHashMap<String, Field> getColumn() {
        return getColumn(type);
    }
    public ConcurrentHashMap<String, Field> getColumn(Class clazz) {
        return InfluxDBHandleUtil.getColumn(clazz);
    }
    public ConcurrentHashMap<String, Field> getMyColumnAnnotation() {
        return getMyColumnAnnotation(type);
    }
    public ConcurrentHashMap<String, Field> getMyColumnAnnotation(Class clazz) {
        return InfluxDBHandleUtil.getMyColumnAnnotation(clazz);
    }
    public ConcurrentHashMap<String, Field> getTagColumn() {
        return getTagColumn(type);
    }
    public ConcurrentHashMap<String, Field> getTagColumn(Class clazz) {
        return InfluxDBHandleUtil.getTagColumn(clazz);
    }
    public ConcurrentHashMap<String, Field> getNoTagColumn() {
        return getNoTagColumn(type);
    }
    public ConcurrentHashMap<String, Field> getNoTagColumn(Class clazz) {
        return InfluxDBHandleUtil.getNoTagColumn(clazz);
    }
    /**
     * 获取tag的组合
     * 例:tag1,tag2,tag...
     * @return
     */
    public String getTagString(){
       return getTagString(type);
    }


    public String getTagString(Class clazz){
        ConcurrentHashMap<String,Field> concurrentHashMap = getTagColumn(clazz);
        Set<Map.Entry<String, Field>> entrySet = concurrentHashMap.entrySet();


        int s = 0;
        String tags = "";
        for(Map.Entry<String, Field> entry:entrySet){
            Field field = entry.getValue();
            if(s == 0){
                tags = tags + field.getName();
            }else {
                tags = tags + "," +field.getName();
            }
            s++;
        }
        return tags;
    }
    /**
     * 获取tag的组合数组
     * @return
     */
    public Object[] getTagArray(){
        return getTagArray(type);
    }


    public Object[] getTagArray(Class clazz){
        ConcurrentHashMap<String,Field> concurrentHashMap = getTagColumn(clazz);
        Set<Map.Entry<String, Field>> entrySet = concurrentHashMap.entrySet();
        String[] tags = new String[entrySet.size()];
        int c = 0;
        for(Map.Entry<String, Field> entry:entrySet){
            Field field = entry.getValue();
            tags[c] = field.getName();
            c++;
        }
        return tags;
    }
    public Object[] getNoTagColumnArray(){
        return getNoTagColumnArray(type);
    }


    public Object[] getNoTagColumnArray(Class clazz){
        ConcurrentHashMap<String,Field> concurrentHashMap = getNoTagColumn(clazz);
        Set<Map.Entry<String, Field>> entrySet = concurrentHashMap.entrySet();
        String[] tags = new String[entrySet.size()];
        int c = 0;
        for(Map.Entry<String, Field> entry:entrySet){
            Field field = entry.getValue();
            tags[c] = field.getName();
            c++;
        }
        return tags;
    }
    /**
     * 执行指定的查询，并返回指定类型的结果集
     * 注意，传入语句构造如果不是select基础查询，可能因为结果集字段转变，导致实体类没有接收到数据
     * @return
     */
    public List<T> query(Query query) {
        return influxDBBaseMapper.query(query,type);
    }

    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 结果集按照时间线分组呈现，故执行的sql 需要由类似group by成分
     * @return
     */
    public <T> Map<T, List<T>> queryGroupBySeries(Query query,Class<T> clazz) {
        String table = this.getMeasurementName();
        String database = this.getDatabaseName();
        TimeUnit timeUnit = this.getTimeUnit();
        QueryResult queryResult = influxDBBaseMapper.query(query);
        return influxDBBaseMapper.toPOJOBySeries(queryResult,clazz,table,timeUnit);
    }
    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 结果集按照时间线分组呈现，故执行的sql 需要由类似group by成分
     * @return
     */
    public Map<T, List<T>> queryGroupBySeries(Query query) {
        return this.queryGroupBySeries(query,type);
    }

    /**
     * 执行指定的查询，并返回指定结构的结果集
     * @return
     */
    public <T> List<T> query(Query query,Class<T> clazz) {
        return influxDBBaseMapper.query(query,clazz);
    }
    /**
     * 根据所给出的泛型，组装查询语句（全表查询），并返回指定类型的结果集
     * @return
     */
    public List<T> selectAll() {
        return influxDBBaseMapper.query(type);
    }

    public int count() {
        List res = influxDBBaseMapper.query(type);
        if(res==null){
            return 0;
        }else {
            return res.size();
        }
    }

    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * @return
     */
    public List<T> myQuery(Query query) {
        return influxDBBaseMapper.myQuery(query,type);
    }
    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * @return
     */
    public <T> List<T> myQuery(Query query,Class<T> clazz) {
        return influxDBBaseMapper.myQuery(query,clazz);
    }

    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 将会比对指定clause中的字段是否有相同的值，如相匹配，则将匹配对象的数据压入结果集相应对象
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * @return
     */
    public <T> List<T> myQueryByHaveSourceData(Query query,Class<T> clazz ,List<T> resData,Set<String> clause) {
        return influxDBBaseMapper.myQueryByHaveSourceData(query,clazz,resData,clause);
    }
    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 将会比对指定clause中的字段是否有相同的值，如相匹配，则将匹配对象的数据压入结果集相应对象
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * 此方法结果集安装时间线分组
     * @return
     */
    public <T> Map<T, List<T>> myQueryByHaveSourceDataGroupBySeries(Query query, Class<T> clazz , List<T> resData, Set<String> clause) {
        return influxDBBaseMapper.myQueryByHaveSourceDataGroupBySeries(query,clazz,resData,clause);
    }
    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 结果集按照时间线分组呈现，故执行的sql 需要由类似group by成分
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * @return
     */
    public <T> Map<T,List<T>> myQueryGroupBySeries(Query query,Class<T> clazz) {
        return influxDBBaseMapper.myQueryGroupBySeries(query,clazz);
    }
    /**
     * 执行指定的查询，并返回指定结构的结果集
     * 结果集按照时间线分组呈现，故执行的sql 需要由类似group by成分
     * 此查询方法被改造，允许使用@MyColumn注解接收sql函数的结果，或者以as定义的结果字段
     * @return
     */
    public Map<T,List<T>> myQueryGroupBySeries(Query query) {
        return influxDBBaseMapper.myQueryGroupBySeries(query,type);
    }


    /**
     * 根据给定的实体查询，所有条件使用等于判断
     * @param model
     * @return
     * @throws InfluxDBException
     */
    public List<T> selectByObject(T model) throws InfluxDBException{
        try {
            StringBuilder select = new StringBuilder("SELECT * FROM ");
            //--以下通过反射从注解获取字段名，字段类型，库名和表名
            Class cls = model.getClass();
            String table = this.getMeasurementName();
            select.append(table).append(" ");
            String database = this.getDatabaseName();
            //反射获取类的Column的注解信息
            ConcurrentMap<String, Field> colNameAndFieldMap = this.getColumn();
            int i = 0;
            for (String key : colNameAndFieldMap.keySet()) {

                Field field = colNameAndFieldMap.get(key);
                Column column = field.getAnnotation(Column.class);
                //字段名
                String columnName = column.name();
                //是否是tag
                boolean isTag = column.tag();
                Class<?> fieldType = field.getType();

                if (!field.isAccessible()) {
                    field.setAccessible(true);
                }
                //字段值
                Object value = field.get(model);

                if (value != null) {
                    if ("time".equals(columnName)) {
                        //time暂时不作为条件

                            if(i>0){
                                select.append("AND ");
                            }else if(i==0){
                                select.append("WHERE ");
                            }
                            InfluxDBHandleUtil.setTime(select,fieldType,value);

                    } else{
                        if(i>0){
                            select.append("AND ");
                        }else if(i==0){
                            select.append("WHERE ");
                        }
                        InfluxDBHandleUtil.setField(select,fieldType,columnName,value);
                        //标记位自增
                        i++;
                    }
                }

            }
            Query query = new Query(select.toString(),database);
            return query(query);
        } catch (IllegalAccessException e) {
            throw new InfluxDBMapperException(e);
        }
    }

    /**
     * 存入一个记录,指向配置文件声明的库
     * @param  model 含有数据的对象，注意对象的字段不能为null（除了time字段）
     * @return
     */
    public <T> void insert(T model) {
        influxDBBaseMapper.save2PublicDatabase(model);
    }

    /**
     * 存入一个记录
     * @param  model 含有数据的对象，注意对象的字段不能为null（除了time字段）
     * @return
     */
    public <T> void insertDefault(T model) {
        influxDBBaseMapper.save(model);
    }
    /**
     * 获取时间线 [table,tage1=tag1_value,tag2=tag2_value]
     * @return
     */
    public List<String> selectSeries() {
        String table = this.getMeasurementName();
        String database = this.getDatabaseName();
        Query query = new Query("SHOW SERIES FROM "+table,database);
        QueryResult queryResult = influxDBBaseMapper.query(query);
        List<Map<String,Object>> resultOne= InfluxDBHandleUtil.analysisResultOne(queryResult);
        List<String> res = new ArrayList<>();
        if(null!=resultOne&&!resultOne.isEmpty()){
            for(Map<String,Object> map:resultOne){
                if(map!=null&&!map.isEmpty()){
                    Set<String> keySet = map.keySet();
                    List<String> list = new ArrayList<>(keySet);
                    res.add((String) map.get(list.get(0)));
                }
            }
            return res;
        }
        return null;
    }

    /**
     * 获取时间线数据,(根据tag分组查询)
     * @return
     */
    public Map<T, List<T>> selectSeriesByGroup() {
        StringBuilder select = new StringBuilder();
        StringBuilder selectGroup = new StringBuilder();
        select.append("SELECT ");
        selectGroup.append("GROUP BY ");
        String table = this.getMeasurementName();
        String database = this.getDatabaseName();
        TimeUnit timeUnit = this.getTimeUnit();
        //反射获取类的Column的注解信息
        ConcurrentMap<String, Field> colNameAndFieldMap = this.getColumn();
        int i = 0;
        for (String key : colNameAndFieldMap.keySet()) {
            Field field = colNameAndFieldMap.get(key);
            Column column = field.getAnnotation(Column.class);
            //字段名
            String columnName = column.name();
            //是否是tag
            boolean isTag = column.tag();

            if(isTag){
                if(i==0){
//                    select.append(columnName);
                    selectGroup.append(columnName);
                }else {
//                    select.append(",").append(columnName);
                    selectGroup.append(",").append(columnName);
                }
                i++;
            }
        }
        String sql = select.append(" * FROM ").append(table).append(" ").toString() + selectGroup.toString();
        Query query = new Query(sql,database);
        QueryResult queryResult = influxDBBaseMapper.query(query);
        Map<T, List<T>> result = influxDBBaseMapper.toPOJOBySeries(queryResult,type,table,timeUnit);
        return result;
    }
}
